using System;
using System.Collections.Generic;
using System.Linq;
using TestCases.CompareAssert.Core.Comparers.Interfaces;
using TestCases.CompareAssert.Core.Compositions;
using TestCases.CompareAssert.Core.Compositions.Interfaces;
using TestCases.CompareAssert.Core.Interfaces;
using TestCases.CompareAssert.Core.Selectors;
using TestCases.Utils;

namespace TestCases.CompareAssert.Core.Comparers.Generics
{
    public abstract class GenericComparer: IInterfaceComparer, ICompositionComparer<Type, GenericCompositionItem>
    {
        private readonly ITypeSelector _selector;
        private readonly ICompositionStrategy<Type, GenericCompositionItem> _strategy;
        
        public GenericComparer(ITypeSelector selector) :
            this(selector, CompositionType.Single)
        {
        }

        public GenericComparer(ITypeSelector selector, CompositionType compositionType) :
            this(selector, compositionType.CreateStrategy<Type, GenericCompositionItem>())
        {
        }
        
        public GenericComparer(ITypeSelector selector, ICompositionStrategy<Type, GenericCompositionItem> strategy)
        {
            _selector = selector;
            _strategy = strategy;
        }

        public bool IgnoreGenericInterfaces { get; set; }


        public GenericCompositionItem CompareBy(Type interfaceType, ICompareContext context)
        {
            var builder = CompareVia(interfaceType, context);
            var item = new GenericCompositionItem(builder);

            builder.Message("Compared via " + interfaceType.ToTypeString() + " interface: " + item.OriginalMessage);            
            return item;
        }

        public IResultBuilder ComposeItemResults(CompareStatus status, ICompareContext context, IList<GenericCompositionItem> children)
        {
            var lastResult = children.Last();
            return this.IsStatusResult(status, context, children).
                Message(lastResult.OriginalMessage).
                With(lastResult.OriginalArgs);
        }

        public abstract string Name { get; }
        public abstract IResultBuilder CompareVia(Type interfaceType, ICompareContext context);
        public abstract IResultBuilder Compare(ICompareContext context);

        public IResultBuilder CompareViaInterfaces(ICompareContext context)
        {
            if (IgnoreGenericInterfaces)
            {
                return null;
            }
            
            var interfaces = _selector.SelectFrom(context.LType, context.RType);
            return (interfaces.Length > 0) ? _strategy.Execute(this, interfaces, context) : null;                
        }
        
    }
}